#include "M6502.h"
#include <stdio.h>
#include <stdlib.h>
#include "Commons.h"
#include <string.h>

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_ADC( mode )  unsigned char t = m_Read( SolveAddress(mode) ); \
                        unsigned short r = ( (m_Reg.A + t + (m_Flag.C ? 1 : 0)) & 0xffff ); \
                        m_Flag.C = ( r > 0x00ff ); \
                        bool c1 = TestBit( (m_Reg.A ^ t) & 0xff, 7 ); \
                        bool c2 = TestBit( (m_Reg.A ^ (r & 0x00ff)) & 0xff, 7 ); \
                        m_Flag.V = ( (! c1) && c2 ); \
                        m_Reg.A = ( r & 0xff ); \
                        m_Flag.Z = FLAG_Z[ m_Reg.A ]; \
                        m_Flag.N = FLAG_N[ m_Reg.A ];

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_AND( mode )  m_Reg.A &= m_Read( SolveAddress(mode) ); \
                        m_Flag.Z = FLAG_Z[ m_Reg.A ]; \
                        m_Flag.N = FLAG_N[ m_Reg.A ];

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_ASL( mode )  unsigned short a = 0; \
                        unsigned char t = 0; \
                        if( mode == ACC ) { \
                        t = m_Reg.A; \
                        } else { \
                        a = SolveAddress( mode ); \
                        t = m_Read( a ); \
                        } \
                        m_Flag.C = TestBit( t, 7 ); \
                        t = ( (t << 1) & 0xfe ); \
                        m_Flag.Z = FLAG_Z[ t ]; \
                        m_Flag.N = FLAG_N[ t ]; \
                        if( mode == ACC ) { \
                        m_Reg.A = t; \
                        } else { \
                        m_Write( a, t ); \
                        }

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_BIT( mode )  unsigned char t = m_Read( SolveAddress(mode) ); \
                        m_Flag.Z = FLAG_Z[ t & m_Reg.A ]; \
                        m_Flag.V = TestBit( t, 6 ); \
                        m_Flag.N = FLAG_N[ t ];

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_BCC( mode )  if( ! m_Flag.C )  m_PC.W = SolveAddress( mode );\
                        else            m_PC.W++;

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_BCS( mode )  if( m_Flag.C )  m_PC.W = SolveAddress( mode ); \
                        else            m_PC.W++;

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_BEQ( mode )  if( m_Flag.Z )  m_PC.W = SolveAddress( mode ); \
                        else            m_PC.W++;

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_BMI( mode )  if( m_Flag.N )  m_PC.W = SolveAddress( mode ); \
                        else            m_PC.W++;

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_BNE( mode )  if( ! m_Flag.Z )  m_PC.W = SolveAddress( mode ); \
                        else            m_PC.W++;

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_BPL( mode )  if( ! m_Flag.N )  m_PC.W = SolveAddress( mode ); \
                        else            m_PC.W++;

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_BVC( mode )  if( ! m_Flag.V )  m_PC.W = SolveAddress( mode ); \
                        else            m_PC.W++;

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_BVS( mode )  if( m_Flag.V )  m_PC.W = SolveAddress( mode ); \
                        else            m_PC.W++;

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_CLC( mode )  m_Flag.C = 0;

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_CLD( mode )  m_Flag.D = 0;

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_CLI( mode )  m_Flag.I = 0;

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_CLV( mode )  m_Flag.V = 0;

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_CMP( mode )  unsigned char t = m_Read( SolveAddress(mode) ); \
                        unsigned char r = ( (m_Reg.A - t) & 0xff ); \
                        m_Flag.C = ( m_Reg.A >= t ); \
                        m_Flag.Z = FLAG_Z[ r ]; \
                        m_Flag.N = FLAG_N[ r ];

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_CPX( mode )  unsigned char t = m_Read( SolveAddress(mode) ); \
                        unsigned char r = ( (m_Reg.X - t) & 0xff ); \
                        m_Flag.C = ( m_Reg.X >= t ); \
                        m_Flag.Z = FLAG_Z[ r ]; \
                        m_Flag.N = FLAG_N[ r ];


////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_CPY( mode )  unsigned char t = m_Read( SolveAddress(mode) ); \
                        unsigned char r = ( (m_Reg.Y - t) & 0xff ); \
                        m_Flag.C = ( m_Reg.Y >= t ); \
                        m_Flag.Z = FLAG_Z[ r ]; \
                        m_Flag.N = FLAG_N[ r ];

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_DEC( mode )  unsigned short a = SolveAddress( mode ); \
                        unsigned char t = m_Read( a ); \
                        t--; \
                        m_Write( a, t ); \
                        m_Flag.Z = FLAG_Z[ t ]; \
                        m_Flag.N = FLAG_N[ t ];

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_DEX( mode )  m_Reg.X--; \
                        m_Flag.Z = FLAG_Z[ m_Reg.X ]; \
                        m_Flag.N = FLAG_N[ m_Reg.X ];

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_DEY( mode )  m_Reg.Y--; \
                        m_Flag.Z = FLAG_Z[ m_Reg.Y ]; \
                        m_Flag.N = FLAG_N[ m_Reg.Y ];

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_EOR( mode )  m_Reg.A ^= m_Read( SolveAddress(mode) ); \
                        m_Flag.Z = FLAG_Z[ m_Reg.A ]; \
                        m_Flag.N = FLAG_N[ m_Reg.A ];

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_INC( mode )  unsigned short a = SolveAddress( mode ); \
                        unsigned char t = m_Read( a ); \
                        t++; \
                        m_Write( a, t ); \
                        m_Flag.Z = FLAG_Z[ t ]; \
                        m_Flag.N = FLAG_N[ t ];

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_INX( mode )  m_Reg.X++; \
                        m_Flag.Z = FLAG_Z[ m_Reg.X ]; \
                        m_Flag.N = FLAG_N[ m_Reg.X ];

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_INY( mode )  m_Reg.Y++; \
                        m_Flag.Z = FLAG_Z[ m_Reg.Y ]; \
                        m_Flag.N = FLAG_N[ m_Reg.Y ];

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_JMP( mode )  m_PC.W = SolveAddress( mode );

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_JSR( mode )  PAIR_REGISTER t = m_PC; \
                        t.W++; \
                        StackPush( t.H ); \
                        StackPush( t.L ); \
                        m_PC.W = SolveAddress( mode );

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_LDA( mode )  m_Reg.A = m_Read( SolveAddress(mode) ); \
                        m_Flag.Z = FLAG_Z[ m_Reg.A ]; \
                        m_Flag.N = FLAG_N[ m_Reg.A ];

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_LDX( mode )  m_Reg.X = m_Read( SolveAddress(mode) ); \
                        m_Flag.Z = FLAG_Z[ m_Reg.X ]; \
                        m_Flag.N = FLAG_N[ m_Reg.X ];

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_LDY( mode )  m_Reg.Y = m_Read( SolveAddress(mode) ); \
                        m_Flag.Z = FLAG_Z[ m_Reg.Y ]; \
                        m_Flag.N = FLAG_N[ m_Reg.Y ];

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_LSR( mode )  unsigned short a = 0; \
                        unsigned char t = 0; \
                        if( mode == ACC ) { \
                        t = m_Reg.A; \
                        } else { \
                        a = SolveAddress( mode ); \
                        t = m_Read( a ); \
                        } \
                        m_Flag.N = false; \
                        m_Flag.C = TestBit( t, 0 ); \
                        t = ( (t >> 1) & 0x7f ); \
                        m_Flag.Z = FLAG_Z[ t ]; \
                        if( mode == ACC ) { \
                        m_Reg.A = t; \
                        } else { \
                        m_Write( a, t ); \
                        }

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_NOP( mode )  mode = IMP;

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_PHA( mode )  StackPush( m_Reg.A );

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_ORA( mode )  m_Reg.A |= m_Read( SolveAddress(mode) ); \
                        m_Flag.Z = FLAG_Z[ m_Reg.A ]; \
                        m_Flag.N = FLAG_N[ m_Reg.A ];

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_PHP( mode )  StackPush( (JoinFlags() | 0x30) & 0xff );

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_PLA( mode )  m_Reg.A = StackPop(); \
                        m_Flag.Z = FLAG_Z[ m_Reg.A ]; \
                        m_Flag.N = FLAG_N[ m_Reg.A ];

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_PLP( mode )  unsigned char t = StackPop(); \
                        m_Flag.P = true; \
                        m_Flag.B = false; \
                        m_Flag.N = FLAG_N[ t ]; \
                        m_Flag.V = TestBit( t, 6 ); \
                        m_Flag.D = TestBit( t, 3 ); \
                        m_Flag.I = TestBit( t, 2 ); \
                        m_Flag.Z = FLAG_Z[ t ]; \
                        m_Flag.C = TestBit( t, 0 );

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_ROL( mode )  unsigned short a = 0; \
                        unsigned char t = 0; \
                        if( mode == ACC ) { \
                        t = m_Reg.A; \
                        } else { \
                        a = SolveAddress( mode ); \
                        t = m_Read( a ); \
                        } \
                        bool c = m_Flag.C; \
                        m_Flag.C = TestBit( t, 7 ); \
                        t = ( (t << 1) & 0xfe ); \
                        if( c ) t = ( (t | 0x01) & 0xff ); \
                        m_Flag.N = FLAG_N[ t ]; \
                        m_Flag.Z = FLAG_Z[ t ]; \
                        if( mode == ACC ) { \
                        m_Reg.A = t; \
                        } else { \
                        m_Write( a, t ); \
                        }

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_ROR( mode )  unsigned short a = 0; \
                        unsigned char t = 0; \
                        if( mode == ACC ) { \
                        t = m_Reg.A; \
                        } else { \
                        a = SolveAddress( mode ); \
                        t = m_Read( a ); \
                        } \
                        bool c = m_Flag.C; \
                        m_Flag.C = TestBit( t, 0 ); \
                        t = ( (t >> 1) & 0x7e ); \
                        if( c ) t = ( (t | 0x80) & 0xff ); \
                        m_Flag.N = FLAG_N[ t ]; \
                        m_Flag.Z = FLAG_Z[ t ]; \
                        if( mode == ACC ) { \
                        m_Reg.A = t; \
                        } else { \
                        m_Write( a, t ); \
                        }

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_RTI( mode )  unsigned char t = StackPop(); \
                        m_Flag.P = true; \
                        m_Flag.B = false; \
                        m_Flag.N = FLAG_N[ t ]; \
                        m_Flag.V = TestBit( t, 6 ); \
                        m_Flag.D = TestBit( t, 3 ); \
                        m_Flag.I = TestBit( t, 2 ); \
                        m_Flag.Z = TestBit( t, 1 ); \
                        m_Flag.C = TestBit( t, 0 ); \
                        m_PC.L = StackPop(); \
                        m_PC.H = StackPop();

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_RTS( mode )  m_PC.L = StackPop(); \
                        m_PC.H = StackPop(); \
                        m_PC.W++;

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_SBC( mode )  unsigned char t = m_Read( SolveAddress(mode) ); \
                        unsigned short r = ( (m_Reg.A - t - (1 - (m_Flag.C ? 1 : 0))) & 0xffff ); \
                        m_Flag.C = ( r < 0x0100 ); \
                        bool c1 = TestBit( (m_Reg.A ^ t) & 0xff, 7 ); \
                        bool c2 = TestBit( (m_Reg.A ^ (r & 0x00ff)) & 0xff, 7 ); \
                        m_Flag.V = ( c1 && c2 ); \
                        m_Reg.A = ( r & 0xff ); \
                        m_Flag.Z = FLAG_Z[ m_Reg.A ]; \
                        m_Flag.N = FLAG_N[ m_Reg.A ];

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_SEC( mode )  m_Flag.C = true;

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_SED( mode )  m_Flag.D = true;

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_SEI( mode )  m_Flag.I = true;

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_STX( mode )  m_Write( SolveAddress(mode), m_Reg.X );

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_STY( mode )  m_Write( SolveAddress(mode), m_Reg.Y );

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_STA( mode )  m_Write( SolveAddress(mode), m_Reg.A );

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_TAX( mode )  m_Reg.X = m_Reg.A; \
                        m_Flag.Z = FLAG_Z[ m_Reg.X ]; \
                        m_Flag.N = FLAG_N[ m_Reg.X ];

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_TAY( mode )  m_Reg.Y = m_Reg.A; \
                        m_Flag.Z = FLAG_Z[ m_Reg.Y ]; \
                        m_Flag.N = FLAG_N[ m_Reg.Y ];

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_TSX( mode )  m_Reg.X = m_SP; \
                        m_Flag.Z = FLAG_Z[ m_Reg.X ]; \
                        m_Flag.N = FLAG_N[ m_Reg.X ];

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_TXA( mode )  m_Reg.A = m_Reg.X; \
                        m_Flag.Z = FLAG_Z[ m_Reg.A ]; \
                        m_Flag.N = FLAG_N[ m_Reg.A ];

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_TXS( mode )  m_SP = m_Reg.X;

////////////////////////////////////////////////////////////////////////////////////////////////////

#define OP_TYA( mode )  m_Reg.A = m_Reg.Y; \
                        m_Flag.Z = FLAG_Z[ m_Reg.A ]; \
                        m_Flag.N = FLAG_N[ m_Reg.A ];

////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////

M6502::M6502() {
    m_Reg.A = 0;
    m_Reg.X = 0;
    m_Reg.Y = 0;
    m_SP = 0xfd;
    m_Flag.N = false;
    m_Flag.V = false;
    m_Flag.P = true;
    m_Flag.B = false;
    m_Flag.D = false;
    m_Flag.I = true;
    m_Flag.Z = false;
    m_Flag.C = false;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

M6502::~M6502() {}

////////////////////////////////////////////////////////////////////////////////////////////////////

unsigned char M6502::Fetch() {
    return m_Read( m_PC.W++ );
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void M6502::HandleNMI() {
    StackPush( m_PC.H );
    StackPush( m_PC.L );
    StackPush( (JoinFlags() | 0x20) & 0xff );
    m_Flag.I = true;
    m_PC.H = m_Read( 0xfffb );
    m_PC.L = m_Read( 0xfffa );
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void M6502::StackPush( unsigned char value ) {
    m_Write( (m_SP-- | 0x0100) & 0xffff, value );
}

////////////////////////////////////////////////////////////////////////////////////////////////////

unsigned char M6502::StackPop() {
    return m_Read( (++m_SP | 0x0100) & 0xffff );
}

////////////////////////////////////////////////////////////////////////////////////////////////////

unsigned char M6502::JoinFlags()const {
    unsigned char f = 0;
    f |= ( (m_Flag.N ? 1 : 0) << 7 );
    f |= ( (m_Flag.V ? 1 : 0) << 6 );
    f |= ( (m_Flag.P ? 1 : 0) << 5 );
    f |= ( (m_Flag.B ? 1 : 0) << 4 );
    f |= ( (m_Flag.D ? 1 : 0) << 3 );
    f |= ( (m_Flag.I ? 1 : 0) << 2 );
    f |= ( (m_Flag.Z ? 1 : 0) << 1 );
    f |= ( m_Flag.C ? 1 : 0 );

    return ( f & 0xff );
}

////////////////////////////////////////////////////////////////////////////////////////////////////

int M6502::Execute( unsigned char opcode ) {
    m_Cycles = 0;

    switch( opcode ) {
        case 0x01: { OP_ORA( INX    ); m_Cycles += 6; } break;
        case 0x05: { OP_ORA( ZP     ); m_Cycles += 3; } break;
        case 0x06: { OP_ASL( ZP     ); m_Cycles += 5; } break;
        case 0x08: { OP_PHP( IMP    ); m_Cycles += 3; } break;
        case 0x09: { OP_ORA( IMM    ); m_Cycles += 2; } break;
        case 0x0a: { OP_ASL( ACC    ); m_Cycles += 2; } break;
        case 0x0d: { OP_ORA( ABS    ); m_Cycles += 4; } break;
        case 0x0e: { OP_ASL( ABS    ); m_Cycles += 6; } break;
        case 0x10: { OP_BPL( REL    ); m_Cycles += 2; } break;
        case 0x11: { OP_ORA( INY    ); m_Cycles += 5; } break;
        case 0x15: { OP_ORA( ZPX    ); m_Cycles += 4; } break;
        case 0x16: { OP_ASL( ZPX    ); m_Cycles += 6; } break;
        case 0x18: { OP_CLC( IMP    ); m_Cycles += 2; } break;
        case 0x19: { OP_ORA( ABY    ); m_Cycles += 4; } break;
        case 0x1d: { OP_ORA( ABX    ); m_Cycles += 4; } break;
        case 0x1e: { OP_ASL( ABX_NC ); m_Cycles += 7; } break;
        case 0x20: { OP_JSR( ABS    ); m_Cycles += 6; } break;
        case 0x21: { OP_AND( INX    ); m_Cycles += 6; } break;
        case 0x24: { OP_BIT( ZP     ); m_Cycles += 3; } break;
        case 0x25: { OP_AND( ZP     ); m_Cycles += 3; } break;
        case 0x26: { OP_ROL( ZP     ); m_Cycles += 5; } break;
        case 0x28: { OP_PLP( IMP    ); m_Cycles += 4; } break;
        case 0x29: { OP_AND( IMM    ); m_Cycles += 2; } break;
        case 0x2a: { OP_ROL( ACC    ); m_Cycles += 2; } break;
        case 0x2c: { OP_BIT( ABS    ); m_Cycles += 4; } break;
        case 0x2d: { OP_AND( ABS    ); m_Cycles += 4; } break;
        case 0x2e: { OP_ROL( ABS    ); m_Cycles += 6; } break;
        case 0x30: { OP_BMI( REL    ); m_Cycles += 2; } break;
        case 0x31: { OP_AND( INY    ); m_Cycles += 5; } break;
        case 0x35: { OP_AND( ZPX    ); m_Cycles += 4; } break;
        case 0x36: { OP_ROL( ZPX    ); m_Cycles += 6; } break;
        case 0x38: { OP_SEC( IMP    ); m_Cycles += 2; } break;
        case 0x39: { OP_AND( ABY    ); m_Cycles += 4; } break;
        case 0x3d: { OP_AND( ABX    ); m_Cycles += 4; } break;
        case 0x3e: { OP_ROL( ABX_NC ); m_Cycles += 7; } break;
        case 0x40: { OP_RTI( IMP    ); m_Cycles += 6; } break;
        case 0x41: { OP_EOR( INX    ); m_Cycles += 6; } break;
        case 0x45: { OP_EOR( ZP     ); m_Cycles += 3; } break;
        case 0x46: { OP_LSR( ZP     ); m_Cycles += 5; } break;
        case 0x48: { OP_PHA( IMP    ); m_Cycles += 3; } break;
        case 0x49: { OP_EOR( IMM    ); m_Cycles += 2; } break;
        case 0x4a: { OP_LSR( ACC    ); m_Cycles += 2; } break;
        case 0x4c: { OP_JMP( ABS    ); m_Cycles += 3; } break;
        case 0x4d: { OP_EOR( ABS    ); m_Cycles += 4; } break;
        case 0x4e: { OP_LSR( ABS    ); m_Cycles += 6; } break;
        case 0x50: { OP_BVC( REL    ); m_Cycles += 2; } break;
        case 0x51: { OP_EOR( INY    ); m_Cycles += 5; } break;
        case 0x55: { OP_EOR( ZPX    ); m_Cycles += 4; } break;
        case 0x56: { OP_LSR( ZPX    ); m_Cycles += 6; } break;
        case 0x58: { OP_CLI( IMP    ); m_Cycles += 2; } break;
        case 0x59: { OP_EOR( ABY    ); m_Cycles += 4; } break;
        case 0x5d: { OP_EOR( ABX    ); m_Cycles += 4; } break;
        case 0x5e: { OP_LSR( ABX_NC ); m_Cycles += 7; } break;
        case 0x60: { OP_RTS( IMP    ); m_Cycles += 6; } break;
        case 0x61: { OP_ADC( INX    ); m_Cycles += 6; } break;
        case 0x65: { OP_ADC( ZP     ); m_Cycles += 3; } break;
        case 0x66: { OP_ROR( ZP     ); m_Cycles += 5; } break;
        case 0x68: { OP_PLA( IMP    ); m_Cycles += 4; } break;
        case 0x69: { OP_ADC( IMM    ); m_Cycles += 2; } break;
        case 0x6a: { OP_ROR( ACC    ); m_Cycles += 2; } break;
        case 0x6c: { OP_JMP( IND    ); m_Cycles += 5; } break;
        case 0x6d: { OP_ADC( ABS    ); m_Cycles += 4; } break;
        case 0x6e: { OP_ROR( ABS    ); m_Cycles += 6; } break;
        case 0x70: { OP_BVS( REL    ); m_Cycles += 2; } break;
        case 0x71: { OP_ADC( INY    ); m_Cycles += 5; } break;
        case 0x75: { OP_ADC( ZPX    ); m_Cycles += 4; } break;
        case 0x76: { OP_ROR( ZPX    ); m_Cycles += 6; } break;
        case 0x78: { OP_SEI( IMP    ); m_Cycles += 2; } break;
        case 0x79: { OP_ADC( ABY    ); m_Cycles += 4; } break;
        case 0x7d: { OP_ADC( ABX    ); m_Cycles += 4; } break;
        case 0x7e: { OP_ROR( ABX_NC ); m_Cycles += 7; } break;
        case 0x81: { OP_STA( INX    ); m_Cycles += 6; } break;
        case 0x84: { OP_STY( ZP     ); m_Cycles += 3; } break;
        case 0x85: { OP_STA( ZP     ); m_Cycles += 3; } break;
        case 0x86: { OP_STX( ZP     ); m_Cycles += 3; } break;
        case 0x88: { OP_DEY( IMP    ); m_Cycles += 2; } break;
        case 0x8a: { OP_TXA( IMP    ); m_Cycles += 2; } break;
        case 0x8c: { OP_STY( ABS    ); m_Cycles += 4; } break;
        case 0x8d: { OP_STA( ABS    ); m_Cycles += 4; } break;
        case 0x8e: { OP_STX( ABS    ); m_Cycles += 4; } break;
        case 0x90: { OP_BCC( REL    ); m_Cycles += 2; } break;
        case 0x91: { OP_STA( INY_NC ); m_Cycles += 6; } break;
        case 0x94: { OP_STY( ZPX    ); m_Cycles += 4; } break;
        case 0x95: { OP_STA( ZPX    ); m_Cycles += 4; } break;
        case 0x96: { OP_STX( ZPY    ); m_Cycles += 4; } break;
        case 0x98: { OP_TYA( IMP    ); m_Cycles += 2; } break;
        case 0x99: { OP_STA( ABY_NC ); m_Cycles += 5; } break;
        case 0x9a: { OP_TXS( IMP    ); m_Cycles += 2; } break;
        case 0x9d: { OP_STA( ABX_NC ); m_Cycles += 5; } break;
        case 0xa0: { OP_LDY( IMM    ); m_Cycles += 2; } break;
        case 0xa1: { OP_LDA( INX    ); m_Cycles += 6; } break;
        case 0xa2: { OP_LDX( IMM    ); m_Cycles += 2; } break;
        case 0xa4: { OP_LDY( ZP     ); m_Cycles += 3; } break;
        case 0xa5: { OP_LDA( ZP     ); m_Cycles += 3; } break;
        case 0xa6: { OP_LDX( ZP     ); m_Cycles += 3; } break;
        case 0xa8: { OP_TAY( IMP    ); m_Cycles += 2; } break;
        case 0xa9: { OP_LDA( IMM    ); m_Cycles += 2; } break;
        case 0xaa: { OP_TAX( IMP    ); m_Cycles += 2; } break;
        case 0xac: { OP_LDY( ABS    ); m_Cycles += 4; } break;
        case 0xad: { OP_LDA( ABS    ); m_Cycles += 4; } break;
        case 0xae: { OP_LDX( ABS    ); m_Cycles += 4; } break;
        case 0xb0: { OP_BCS( REL    ); m_Cycles += 2; } break;
        case 0xb1: { OP_LDA( INY    ); m_Cycles += 5; } break;
        case 0xb4: { OP_LDY( ZPX    ); m_Cycles += 4; } break;
        case 0xb5: { OP_LDA( ZPX    ); m_Cycles += 4; } break;
        case 0xb6: { OP_LDX( ZPY    ); m_Cycles += 4; } break;
        case 0xb8: { OP_CLV( IMP    ); m_Cycles += 2; } break;
        case 0xb9: { OP_LDA( ABY    ); m_Cycles += 4; } break;
        case 0xba: { OP_TSX( IMP    ); m_Cycles += 2; } break;
        case 0xbc: { OP_LDY( ABX    ); m_Cycles += 4; } break;
        case 0xbd: { OP_LDA( ABX    ); m_Cycles += 4; } break;
        case 0xbe: { OP_LDX( ABY    ); m_Cycles += 4; } break;
        case 0xc0: { OP_CPY( IMM    ); m_Cycles += 2; } break;
        case 0xc1: { OP_CMP( INX    ); m_Cycles += 6; } break;
        case 0xc4: { OP_CPY( ZP     ); m_Cycles += 3; } break;
        case 0xc5: { OP_CMP( ZP     ); m_Cycles += 3; } break;
        case 0xc6: { OP_DEC( ZP     ); m_Cycles += 5; } break;
        case 0xc8: { OP_INY( IMP    ); m_Cycles += 2; } break;
        case 0xc9: { OP_CMP( IMM    ); m_Cycles += 2; } break;
        case 0xca: { OP_DEX( IMP    ); m_Cycles += 2; } break;
        case 0xcc: { OP_CPY( ABS    ); m_Cycles += 4; } break;
        case 0xcd: { OP_CMP( ABS    ); m_Cycles += 4; } break;
        case 0xce: { OP_DEC( ABS    ); m_Cycles += 6; } break;
        case 0xd0: { OP_BNE( REL    ); m_Cycles += 2; } break;
        case 0xd1: { OP_CMP( INY    ); m_Cycles += 5; } break;
        case 0xd5: { OP_CMP( ZPX    ); m_Cycles += 4; } break;
        case 0xd6: { OP_DEC( ZPX    ); m_Cycles += 6; } break;
        case 0xd8: { OP_CLD( IMP    ); m_Cycles += 2; } break;
        case 0xd9: { OP_CMP( ABY    ); m_Cycles += 4; } break;
        case 0xdd: { OP_CMP( ABX    ); m_Cycles += 4; } break;
        case 0xde: { OP_DEC( ABX_NC ); m_Cycles += 7; } break;
        case 0xe0: { OP_CPX( IMM    ); m_Cycles += 2; } break;
        case 0xe1: { OP_SBC( INX    ); m_Cycles += 6; } break;
        case 0xe4: { OP_CPX( ZP     ); m_Cycles += 3; } break;
        case 0xe5: { OP_SBC( ZP     ); m_Cycles += 3; } break;
        case 0xe6: { OP_INC( ZP     ); m_Cycles += 5; } break;
        case 0xe8: { OP_INX( IMP    ); m_Cycles += 2; } break;
        case 0xe9: { OP_SBC( IMM    ); m_Cycles += 2; } break;
        case 0xea: {                   m_Cycles += 2; } break;
        case 0xec: { OP_CPX( ABS    ); m_Cycles += 4; } break;
        case 0xed: { OP_SBC( ABS    ); m_Cycles += 4; } break;
        case 0xee: { OP_INC( ABS    ); m_Cycles += 6; } break;
        case 0xf0: { OP_BEQ( REL    ); m_Cycles += 2; } break;
        case 0xf1: { OP_SBC( INY    ); m_Cycles += 5; } break;
        case 0xf5: { OP_SBC( ZPX    ); m_Cycles += 4; } break;
        case 0xf6: { OP_INC( ZPX    ); m_Cycles += 6; } break;
        case 0xf8: { OP_SED( IMP    ); m_Cycles += 2; } break;
        case 0xf9: { OP_SBC( ABY    ); m_Cycles += 4; } break;
        case 0xfd: { OP_SBC( ABX    ); m_Cycles += 4; } break;
        case 0xfe: { OP_INC( ABX_NC ); m_Cycles += 7; } break;
        default: { printf( "\nERROR: < %02X > no es un opcode valido =(", opcode ); getchar(); exit( 0 ); };
    }

    return m_Cycles;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

unsigned short M6502::SolveAddress( int mode ) {
    switch( mode ) {
        case ABS: {
            unsigned short t = ( (Fetch() | (Fetch() << 8)) & 0xffff );
            return t;
        }

        case ABX: {
            PAIR_REGISTER t;
            t.L = Fetch();
            t.H = Fetch();
            unsigned char b = t.H;
            t.W += m_Reg.X;
            if( b != t.H ) m_Cycles++;
            return t.W;
        }

        case ABX_NC: {
            PAIR_REGISTER t;
            t.L = Fetch();
            t.H = Fetch();
            t.W += m_Reg.X;
            return t.W;
        }

        case ABY: {
            PAIR_REGISTER t;
            t.L = Fetch();
            t.H = Fetch();
            unsigned char b = t.H;
            t.W += m_Reg.Y;
            if( b != t.H ) m_Cycles++;
            return t.W;
        }

        case ABY_NC: {
            PAIR_REGISTER t;
            t.L = Fetch();
            t.H = Fetch();
            t.W += m_Reg.Y;
            return t.W;
        }

        case IMM: {
            return m_PC.W++;
        }

        case IND: {
            PAIR_REGISTER t1, t2;
            t1.L = Fetch();
            t1.H = m_Read( m_PC.W );
            t2.L = m_Read( t1.W );
            t1.L++;
            t2.H = m_Read( t1.W );
            return t2.W;
        }

        case INX: {
            PAIR_REGISTER t;
            t.L = m_Read( (m_Read(m_PC.W) + m_Reg.X) & 0xff );
            t.H = m_Read( (Fetch() + m_Reg.X + 1) & 0xff );
            return t.W;
        }

        case INY: {
            PAIR_REGISTER t;
            t.L = m_Read( m_Read(m_PC.W) );
            t.H = m_Read( (Fetch() + 1) & 0xff );
            unsigned char b = t.H;
            t.W = ( (((t.H << 8) | t.L) + m_Reg.Y) & 0xffff );
            if( b != t.H ) m_Cycles++;
            return t.W;
        }

        case INY_NC: {
            PAIR_REGISTER t;
            t.L = m_Read( m_Read(m_PC.W) );
            t.H = m_Read( (Fetch() + 1) & 0xff );
            t.W = ( (((t.H << 8) | t.L) + m_Reg.Y) & 0xffff );
            return t.W;
        }

        case REL: {
            unsigned char b = ( (((m_PC.W + 1) & 0xffff) >> 8) & 0xff );
            signed short d = Fetch();
            if( TestBit(d, 7) ) d -= 0x0100;
            m_PC.W = ( (m_PC.W + d) & 0xffff );
            m_Cycles += ( b != m_PC.H ? 2 : 1 );
            return m_PC.W;
        }

        case ZP: {
            return Fetch();
        }

        case ZPX: {
            return ( (Fetch() + m_Reg.X) & 0x00ff );
        }

        case ZPY: {
            return ( (Fetch() + m_Reg.Y) & 0x00ff );
        }
    }

    return 0;
}

////////////////////////////////////////////////////////////////////////////////////////////////////